#include "main.h"
#include "systick.h"
#include "gd32f20x_eval.h"
#include "gd32f20x.h"
#include <stdio.h>
#include "drv_i2c.h"
#include "bus_i2c.h"
#include <rtthread.h>

/**
    \brief      configure the GPIO ports
    \param[in]  none
    \param[out] none
    \retval     none
  */
void fwdgt_init(void)
{
    /* enable IRC40K */
    rcu_osci_on(RCU_IRC40K);

    /* wait till IRC40K is ready */
    while (SUCCESS != rcu_osci_stab_wait(RCU_IRC40K))
    {
    }

    /* configure FWDGT counter clock: 40KHz(IRC40K) / 64 = 0.625 KHz */
    fwdgt_config(4 * 500, FWDGT_PSC_DIV64); // t = (1/0.625K)x(2x500) = 1.6s

    fwdgt_write_disable();
    /* After 1.6 seconds to generate a reset */
    fwdgt_enable();
}

/**
    \brief      configure the GPIO ports
    \param[in]  none
    \param[out] none
    \retval     none
  */
void feed_fwdgt(void)
{
    /* uncock fwdgt write protect*/
    fwdgt_write_enable();
    /* feed fwdgt */
    fwdgt_counter_reload();
}

static struct rt_thread wdg_thread;
static char wdg_thread_entry_stack[512];
static void wdg_thread_entry(void *parameter)
{
    rt_uint32_t count = 0;

    while (1)
    {
        /* feed fwdgt */
        feed_fwdgt();
        rt_thread_mdelay(2000); // 2sιһ�ι�
    }
}

static void create_wdg_thread(void)
{
    rt_err_t result;

    result = rt_thread_init(&wdg_thread,
                            "wdg_thread",
                            wdg_thread_entry,
                            RT_NULL,
                            wdg_thread_entry_stack,
                            sizeof(wdg_thread_entry_stack),
                            8,
                            10);

    /* fwdgt init */
    fwdgt_init();
    if (result == RT_EOK)
    {
        rt_thread_startup(&wdg_thread);
        rt_kprintf("wdg_thread created successfully\n");
    }
    else
    {
        rt_kprintf("wdg_thread creation failed: %d\n", result);
    }
}

void sata_en_ctrl()
{
    rcu_periph_clock_enable(RCU_GPIOB);
    gpio_init(GPIOB, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_13);
    gpio_bit_set(GPIOB, GPIO_PIN_13);
    rt_thread_mdelay(10);
    rcu_periph_clock_enable(RCU_GPIOB);
    gpio_init(GPIOB, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9);
    gpio_bit_set(GPIOB, GPIO_PIN_9);
    rt_thread_mdelay(10);
    rcu_periph_clock_enable(RCU_GPIOC);
    gpio_init(GPIOC, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_8 | GPIO_PIN_13);
    gpio_bit_set(GPIOC, GPIO_PIN_8 | GPIO_PIN_13);
    rcu_periph_clock_enable(RCU_GPIOB);
    gpio_init(GPIOB, GPIO_MODE_OUT_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_3 | GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_15);
    gpio_bit_set(GPIOB, GPIO_PIN_3 | GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_15);
}
INIT_APP_EXPORT(sata_en_ctrl);

#define SATA_RESET_PIN GPIO_PIN_14
#define SATA_RESET_PORT GPIOB
#define SATA_RESET_THREAD_STACK_SIZE 256
#define SATA_RESET_THREAD_PRIORITY 10
#define SATA_RESET_THREAD_TIMESLICE 10

static void sata_reset_callback(void)
{
    rt_kprintf("[SATA] Reset pin low, trigger callback!\r\n");
}
void sata_reset_detect_init(void)
{
    rcu_periph_clock_enable(RCU_GPIOB);
    gpio_init(SATA_RESET_PORT, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, SATA_RESET_PIN);
}

static void sata_reset_thread_entry(void *parameter)
{
    rt_thread_mdelay(5000);
    while (1)
    {
        if (gpio_input_bit_get(SATA_RESET_PORT, SATA_RESET_PIN) == RESET)
        {
            sata_reset_callback();
            rt_thread_mdelay(50);
        }
        rt_thread_mdelay(10);
    }
}

int sata_reset_detect_start(void)
{
    sata_reset_detect_init();
    rt_thread_t thread = rt_thread_create("sata_det",
                                          sata_reset_thread_entry,
                                          RT_NULL,
                                          SATA_RESET_THREAD_STACK_SIZE,
                                          SATA_RESET_THREAD_PRIORITY,
                                          SATA_RESET_THREAD_TIMESLICE);

    if (thread != RT_NULL)
    {
        rt_thread_startup(thread);
        return 0;
    }
    return -1;
}
INIT_APP_EXPORT(sata_reset_detect_start);

void reboot(void)
{
    rt_kprintf("system rest\n");
    NVIC_SystemReset();
}
MSH_CMD_EXPORT(reboot, reboot);

void adc_init_config(void)
{
    /* 使能 GPIOC 和 ADC0 时钟 */
    rcu_periph_clock_enable(RCU_GPIOC);
    rcu_periph_clock_enable(RCU_ADC0);

    /* 设置 PC0~PC5 为模拟输入 */
    gpio_init(GPIOC, GPIO_MODE_AIN, GPIO_OSPEED_50MHZ,
              GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 |
              GPIO_PIN_3 | GPIO_PIN_4 | GPIO_PIN_5);

    /* ADC 初始化 */
    adc_deinit(ADC0);

    adc_mode_config(ADC_MODE_FREE);                         // 独立模式
    adc_data_alignment_config(ADC0, ADC_DATAALIGN_RIGHT);   // 数据右对齐
    adc_channel_length_config(ADC0, ADC_REGULAR_CHANNEL, 6);// 配置规则组长度 = 6 通道

    /* 配置采样通道顺序（PC0-PC5 = ADC_CHANNEL_10 ~ ADC_CHANNEL_15） */
    adc_regular_channel_config(ADC0, 0, ADC_CHANNEL_10, ADC_SAMPLETIME_55POINT5);
    adc_regular_channel_config(ADC0, 1, ADC_CHANNEL_11, ADC_SAMPLETIME_55POINT5);
    adc_regular_channel_config(ADC0, 2, ADC_CHANNEL_12, ADC_SAMPLETIME_55POINT5);
    adc_regular_channel_config(ADC0, 3, ADC_CHANNEL_13, ADC_SAMPLETIME_55POINT5);
    adc_regular_channel_config(ADC0, 4, ADC_CHANNEL_14, ADC_SAMPLETIME_55POINT5);
    adc_regular_channel_config(ADC0, 5, ADC_CHANNEL_15, ADC_SAMPLETIME_55POINT5);

    /* 禁止外部触发，使用软件触发 */
    adc_external_trigger_source_config(ADC0, ADC_REGULAR_CHANNEL, ADC0_1_2_EXTTRIG_REGULAR_NONE);
    adc_external_trigger_config(ADC0, ADC_REGULAR_CHANNEL, ENABLE);

    /* 使能 ADC */
    adc_enable(ADC0);
    rt_thread_mdelay(1);

    /* 校准 */
    adc_calibration_enable(ADC0);

    rt_kprintf("ADC0 init done.\n");
}
INIT_APP_EXPORT(adc_init_config);

static uint16_t adc_get_channel_value(uint8_t channel)
{
    adc_regular_channel_config(ADC0, 0, channel, ADC_SAMPLETIME_55POINT5);

    adc_software_trigger_enable(ADC0, ADC_REGULAR_CHANNEL);

    /* 等待转换完成 */
    while(!adc_flag_get(ADC0, ADC_FLAG_EOC));
    adc_flag_clear(ADC0, ADC_FLAG_EOC);

    return adc_regular_data_read(ADC0);
}

static void adc_test_all(void)
{
    const uint8_t channels[6] = {
        ADC_CHANNEL_10, // PC0
        ADC_CHANNEL_11, // PC1
        ADC_CHANNEL_12, // PC2
        ADC_CHANNEL_13, // PC3
        ADC_CHANNEL_14, // PC4
        ADC_CHANNEL_15  // PC5
    };

    for (int i = 0; i < 6; i++)
    {
        uint16_t raw = adc_get_channel_value(channels[i]);
        float voltage = (raw * 3.3f) / 4095.0f;

        int mv = (int)(voltage * 1000);
        rt_kprintf("PC%d raw: %4d -> %d.%03d V\n", i, raw, mv / 1000, mv % 1000);
    }
}
MSH_CMD_EXPORT(adc_test_all, read ADC0 channels PC0~PC5);

int main(void)
{
    memset(&ipmi_data, 0, sizeof(ipmi_data));
    ipmi_data.slave_add = I2C0_SLAVE_ADDRESS;

    printf("slave addr = %02x\r\n", ipmi_data.slave_add);
    ipmi_data.online = 1;
    create_wdg_thread();

    i2c_init(I2C0, I2C_SLAVE_CH, ipmi_data.slave_add);
    bus_start(I2C0);
    i2c_init(I2C1, I2C_SLAVE_CH, ipmi_data.slave_add);
    bus_start(I2C1);
}
